#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <ctype.h>
#include "gid_export.h"
#include "Scalar.h"
// #include "tipo_vertice.h"

#ifdef _DEBUG
#define CLICKS_TO_UPDATE        0x1fff
#else 
#define CLICKS_TO_UPDATE        0x1ffff
#endif

static char *_G_err_str = NULL;
static void ( *_G_update_callback)() = NULL;

static void call_update_callback() {
  if ( _G_update_callback)
    _G_update_callback();
}

// static void set_err_str( const char *str = NULL) {
//   if ( _G_err_str) free( _G_err_str);
//   _G_err_str = NULL;
//   if ( str) {
//     _G_err_str = strdup( str);
//   }
// }

const char *get_gid_error() { // gets error string if there was any error while reading ply file
  return _G_err_str; 
}

void set_gid_update_callback( void ( *fnc)()) {
  _G_update_callback = fnc;
}

int write_gid_files( const char *filename,
                     const char *obj_name, 
                     int num_puntos, const Punto *lst_puntos,
                     int num_triangulos, const Triangulo *lst_triangulos,
                     int num_lineas, const Linea *lst_lineas,
                     const Scalars **lst_scalars) { // last_scalar[ i] should be NULL
  int ret = 0;
  // check extension, should be '.msh'
  char full_name[ 8192];
  strcpy( full_name, filename);
  char *ext_ok = strrchr( full_name, '.');
  if ( ext_ok) {
    if ( strcasecmp( ext_ok, ".msh")) {
      ext_ok = NULL; // if ext but is not .msh
    }
  }
  if ( !ext_ok) {
    strcat( full_name, ".msh"); // add .msh
  }

  FILE *fo = fopen( full_name, "w");
  if ( fo) {
    const char *name = strrchr( filename, '/');
#ifdef _WIN32
    if ( !name) {
      name = strrchr( filename, '\\');
    }
#endif // _WIN32
    if ( !name) name = filename;
    fprintf( fo, "# generated by gMeshSim\n\n");
    fprintf( fo, "MESH \"%s\" dimension 3 Elemtype Triangle Nnode 3\n", obj_name ? obj_name : name);
    fprintf( fo, "Coordinates\n");
    for ( int ip = 0; ip < num_puntos; ip++) {
      fprintf( fo, "%d %.14g %.14g %.14g\n", ip + 1,
               lst_puntos[ ip].x(), lst_puntos[ ip].y(), lst_puntos[ ip].z());
      if ( !( ip & CLICKS_TO_UPDATE)) {
        call_update_callback();
      }
    }
    fprintf( fo, "end coordinates\n\n");
    fprintf( fo, "Elements\n");
    for ( int it = 0; it < num_triangulos; it++) {
      fprintf( fo, "%d %lld %lld %lld\n", it + 1, 
	       ( long long int)lst_triangulos[ it].v1() + 1, 
	       ( long long int)lst_triangulos[ it].v2() + 1, 
	       ( long long int)lst_triangulos[ it].v3() + 1);
      if ( !( it & CLICKS_TO_UPDATE)) {
        call_update_callback();
      }
    }
    fprintf( fo, "end elements\n");

    if ( num_lineas && lst_lineas) {
      fprintf( fo, "\nMESH \"%s lines\" dimension 3 Elemtype Linear Nnode 2\n", obj_name ? obj_name : name);
      fprintf( fo, "Coordinates\n");
      fprintf( fo, "end coordinates\n\n");
      fprintf( fo, "Elements\n");
      for ( int it = 0; it < num_lineas; it++) {
        fprintf( fo, "%d %lld %lld\n", num_triangulos + it + 1, 
                 ( long long int)lst_lineas[ it].v1() + 1, 
                 ( long long int)lst_lineas[ it].v2() + 1);
        if ( !( it & CLICKS_TO_UPDATE)) {
          call_update_callback();
        }
      }
      fprintf( fo, "end elements\n");
    }
    
    fclose( fo);
    ret = 1;
  }

  if ( ret && lst_scalars && lst_scalars[ 0]) {
    // now the scalar as result
    ext_ok = strrchr( full_name, '.');
    *ext_ok = '\0';
    strcat( full_name, ".res");
    fo = fopen(  full_name, "w");
    fprintf( fo, "GiD Post Results File 1.0\n\n");
    char buf[ 1024];
    for ( int idx = 0; lst_scalars[ idx]; idx++) {
      size_t ic = 0;
      for ( ic = 0; ic < strlen( lst_scalars[ idx]->get_name()); ic++) {
	buf[ ic] = ( char)tolower( lst_scalars[ idx]->get_name()[ ic]);
      }
      buf[ ic] = '\0';
      // const char *str_tipo = strstr( buf, "tipo");
      // if ( str_tipo) {
      //   write_vertex_type_description_table( fo, lst_scalars[ idx]->get_name());
      // }
      // const char *str_level = strstr( buf, "evel"); // from level, Level
      // if ( str_level) {
      //   write_level_description_table( fo, lst_scalars[ idx]->get_name());
      // }
      fprintf( fo, "Result \"%s\" \"gMeshSim\" 1.0 Scalar OnNodes\n", lst_scalars[ idx]->get_name());
      // if ( str_tipo || str_level) {
      //   fprintf( fo, "ResultRangesTable \"tabla %s\"\n", lst_scalars[ idx]->get_name());
      // }
      fprintf( fo, "Values\n");
      for ( int iv = 0; iv < num_puntos; iv++) {
        fprintf( fo, "%d %.14g\n", iv + 1, ( *( lst_scalars[ idx]))[ iv]);
        if ( !( iv & CLICKS_TO_UPDATE)) {
          call_update_callback();
        }
      }
      fprintf( fo, "end values\n");
    }
    fclose( fo);
  }

  return ret;
}
